package com.dny.asmtop.op;

import com.dny.asmtop.ASMMethodUtils;
import com.dny.asmtop.CB;
import com.dny.asmtop.Command;
import com.dny.asmtop.MethodContext;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;

import java.util.Iterator;
import java.util.Objects;

import static jdk.internal.org.objectweb.asm.Type.getType;

/**
 * Created by jlutt on 2018-01-17.
 * 集合for each操作
 * @author jlutt
 */
public class CommandCollectionForEach implements Command {

  /**
   * 操作集合对象
   */
  private final Command collection;

  /**
   * for each操作
   */
  private final ForVar forCollection;

  /**
   * 集合元素类型
   */
  private final Class<?> type;

  public CommandCollectionForEach(Command collection, ForVar forCollection) {
    this.collection = collection;
    this.forCollection = forCollection;
    this.type = Object.class;
  }

  public CommandCollectionForEach(Command collection, Class<?> type, ForVar forCollection) {
    this.collection = collection;
    this.forCollection = forCollection;
    this.type = type;
  }

  @Override
  public Type type(MethodContext context) {
    return Type.VOID_TYPE;
  }

  @Override
  public Type generator(MethodContext context) {
    GeneratorAdapter g = context.getGeneratorAdapter();
    Label labelLoop = new Label();
    Label labelExit = new Label();

    //数组直接循环操作
    if (collection.type(context).getSort() == Type.ARRAY) {
      CB.fori(CB.length(collection), it -> forCollection.forLoop(CB.getArrayItem(collection, it))).generator(context);
      return Type.VOID_TYPE;
    }

    //集合对象取迭代器进行操作
    VarLocal varIter = ASMMethodUtils.newLocal(context, getType(Iterator.class));

    Class<?> t = ASMMethodUtils.tryGetJavaType(collection.type(context));
    if (t.isInstance(Iterator.class) || t == Iterator.class) {
      //如果传入的集合本身就是迭代器对象，则赋值
      collection.generator(context);
      varIter.store(context);
    } else {
      //集合对象调用iterator方法获取
      CB.call(collection, "iterator").generator(context);
      varIter.store(context);

    }

    g.mark(labelLoop);

    CB.call(varIter, "hasNext").generator(context);
    g.push(false);
    g.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, labelExit);

    CB.cast(CB.call(varIter, "next"), type).generator(context);
    VarLocal varKey = ASMMethodUtils.newLocal(context, getType(type));
    varKey.store(context);

    forCollection.forLoop(varKey).generator(context);

    g.goTo(labelLoop);
    g.mark(labelExit);
    return Type.VOID_TYPE;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    CommandCollectionForEach that = (CommandCollectionForEach) o;
    return Objects.equals(collection, that.collection) &&
        Objects.equals(type, that.type);
  }

  @Override
  public int hashCode() {

    return Objects.hash(collection, type);
  }
}
